package com.somewater.storage
{
	import com.adobe.serialization.json.JSON;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	[Event(name="langLoadingError",type="com.somewater.storage.Lang")]
	[Event(name="langLoadingComplete",type="com.somewater.storage.Lang")]
	
	final public class Lang extends EventDispatcher
	{
		private static var instance:Lang;
		public static const LANG_LOADING_ERROR:String = "langLoadingError";
		public static const LANG_LOADING_COMPLETE:String = "langLoadingComplete";
		
		private var langLoader:URLLoader;
		private var dictionry:Object;
		
		[Embed("locale/ru_RU/textstorage.properties", mimeType="application/octet-stream")]
		private static const DefaultLang : Class;
		
		// для записи текстовых констант, которые будут доступны еще до выбора и загрузки языка
		// basic[0] содержит список всех доступных языков вида [{},{}...{label:название, data:путь загрузки файла или null}..]
		public static const basic:Array = [];
		
		public function Lang()
		{
			 
		}
		
		public static function createInstance():void{
			if(instance == null)
				instance = new Lang();
			else
				throw new Error("Lang instance creating error. Instance already created");
		}
		
		public static function get getInstance():Lang{
			return instance;
		}
		
		/**
		 * Загрузить и установить язык (при вызове без параметра устанавливает язык вшитый в переменную DefaultLang)
		 * Иначе парсинг происходит только после загрузки файла
		 */
		public static function load(path:String = null):void{
			if (instance == null) createInstance();
			if (path == null){
				instance.parse((new DefaultLang()));
			}else{
				if (instance.langLoader == null){
					instance.langLoader = new URLLoader()
					instance.langLoader.addEventListener(IOErrorEvent.IO_ERROR,instance.errorLangLoaging);
					instance.langLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,instance.errorLangLoaging);
					instance.langLoader.addEventListener(Event.COMPLETE,instance.langLoaded);
				}			
				instance.langLoader.load(new URLRequest(path));	
			}			
		}
		
		/**
		 * Получить слово по введенному ключу. Согласно текущему словарю
		 */
		public static function t(key:String):String{
			var dict:Object = instance.dictionry;
			if (dict.hasOwnProperty(key))
				return dict[key];
			else
				throw new Error("Lang error. No word on specific key: "+key);
		}
		
		public static function arr(key:String, separator:String = ","):Array{
			var dict:Object = instance.dictionry;
			if (dict.hasOwnProperty(key))
				if (dict[key] is Array)
					return dict[key];
				else
					{
						dict[key] = stringToArray(dict[key],separator);
						return dict[key];
					}
			else
				throw new Error("Lang error. No array on specific key: "+key);
		}
		
		// получив стринг типа "12312^ksjdfjksd^kjklj^sdfsdf" где "^" сепаратор возвратит массив [12312,ksjdfjksd,kjklj,sdfsdf]
		public static function stringToArray(storage:String, separator:String = ","):Array{
			var array:Array = [];
			var startIndex:int;
			var stopIndex:int = -1;
			var counter:int = 0;
			do{
				startIndex = stopIndex + 1;
				stopIndex = storage.indexOf(separator,startIndex);							
				array.push(storage.substring(startIndex,(stopIndex == -1)?storage.length:stopIndex));
				if (stopIndex == -1) break;
			}while(counter<1000)
			return array;
		}
		
		private function errorLangLoaging(e:Event):void{
			dispatchEvent(new Event(Lang.LANG_LOADING_ERROR));
		}
		
		private function langLoaded(e:Event):void{
			langLoader.removeEventListener(IOErrorEvent.IO_ERROR,errorLangLoaging);
			langLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,errorLangLoaging);
			langLoader.removeEventListener(Event.COMPLETE,langLoaded);
			langLoader = null;
			parse(e.target.data);
		}
		
		/**
		 * В файле должно быть менее 1'000'000 строк
		 * Если парсинг произведен успешно, диспатчится событие COMPLETE
		 * После парсинка в главном управляющем классе надо переопределить
		 * PersonalData.lng = Lang.t("LNG"); - чтобы знать строковое представление вновь загруженного языка
		 */
		private function parse(storage:String):void{
			var counter:int = 0;
			var startIndex:int = 0;
			var stopIndex:int = -1;
			var newDict:Boolean = false;// создается ли новый словарь, или замещаются поля старого (новым языковым файлом)
			if (dictionry == null) {
				dictionry = {};
				newDict = true;
			}
			var r:RegExp = new RegExp("\\\\n", "g");
			do {
				startIndex = stopIndex + 1;
				stopIndex = storage.indexOf("\n",startIndex);				
				var line:String = storage.substring(startIndex,(stopIndex == -1)?line.length-1:stopIndex-1);// -1 т.к. не захватываем \n,\r
				if (line.charAt(0) != "#"){
					var key:String = line.substr(0,line.indexOf("="));
					var value:String = line.substr(line.indexOf("=")+1); // +1 т.к. не захватываем "="
					if (dictionry.hasOwnProperty(key) && newDict)
						throw new Error("Lang error. Doble key in parsing process");
					else
						dictionry[key] = value.replace(r,"\n");	
				}
				if (stopIndex == -1) break;			
				counter++;
			}	
			while(counter<1000000)
			dispatchEvent(new Event(Lang.LANG_LOADING_COMPLETE));
		}	
		
		/**
		 * Согласно заданному числу выдает корректный стринг для слово (например 7 "дней", 1 "день"...)
		 * Абстрактная ф-ция для формирования общего алгоритма для минут, часов и дней
		 * 
		 * Правила задаются как _rule:array (готовые)
		 * либо как key_rule
		 */
		private static function abstractRule(value:int,_rule:Array,key_rule:String,key_default:String):String{
			if (_rule == null) _rule = Lang.arr(key_rule);
			var rules:Array = _rule;
			var i:int = 0;
			while((i+2)<rules.length){
				if (int(rules[i])<= value && int(rules[i+2])>=value)
					return rules[i+1];
				i += 3;
			}
			return Lang.t(key_default);
		} 
		
		private var _day_rule:Array;
		public static function day(value:int):String{
			return abstractRule(value,null,"DAY_RULE","DAY_DEFAULT");
		}
		
		private var _hour_rule:Array;
		public static function hour(value:int):String{
			return abstractRule(value,null,"HOUR_RULE","HOUR_DEFAULT");
		}
		
		private var _minute_rule:Array;
		public static function minute(value:int):String{
			return abstractRule(value,null,"MINUTE_RULE","MINUTE_DEFAULT");
		}
		
		/**
		 * Возвращает денежную единицу, пресущую данному загруженному языку (согласно переменной MONEY_ID)
		 */
		 public static function money(short:Boolean = true,currency:int = -1):String{
		 	return Lang.arr("PROMPT_MONEY"+(short?"_SHORT":""))[currency == -1?0: currency];
		 }
		 
	}
}